#include "zm_spec.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <error.h>
#include "des.h"
#include "log.h"


inline static int zm_check_req_cmd(unsigned char cmd)
{
	if(cmd <= REQ_CMD_MIN || cmd >= REQ_CMD_MAX)
		return -1;
	else
		return 0;
}

inline static int zm_check_res_cmd(unsigned char cmd)
{
	if(cmd <= RES_CMD_MIN || cmd >= RES_CMD_MAX)
		return -1;
	else
		return 0;
}

static int zm_write_header(char *buff,int *len)
{
	buff[0] = CMD_HEADER_F;
	buff[1] = CMD_HEADER_S;
	*len += 2;
	
	return 0;
}
static int zm_write_content(const char *pcontent,int content_len, char *buff, int *len)
{
	buff[PACKET_CMD_POS] = pcontent[0];
	memcpy(&buff[PACKET_DATA_POS],pcontent+1,content_len-1);
	*len += content_len+2;
	return 0;
}
static int zm_calc_checksum(const char *buff,int len)
{
	int i=0,sum = 0;

	for(i=0;i<len;i++)
	{
		sum += buff[i];
	}

	return sum;
}

static int zm_write_tail(char *buff,int *len)
{
	int packet_len = 0;

	packet_len = *len+2;
	buff[2] = packet_len & 0xff;
	buff[3] = (packet_len>>8) & 0xff;
	buff[packet_len-2] = zm_calc_checksum(buff,packet_len-2);
	buff[packet_len-1] = CMD_TAIL;
	*len += 2;

	return 0;
}

/*
 * build packet
 */
/*
 * buff : value-result parameter
 * 	[in] cmd and data content
 * 	[out] packed packet
 * len : value-result parameter
 * 	[in] content valid length
 * 	[out] packet valid length
 */
int zm_build_packet(char *buff, int *len)
{
	if(buff == NULL || *len <= 0)
	{
		dzlog_error("invalid params!\n");
		return -1;
	}

	int content_len;
	char *pdata = NULL;

	if(zm_check_req_cmd(buff[0]) < 0)
	{
		dzlog_error("invalid zm spec requese cmd:%d\n",buff[0]);
		return -1;
	}
	
	content_len = *len;
	pdata = (char *)malloc(content_len); 
	if(pdata == NULL)
	{
		dzlog_error("malloc failed!\n");
		return -1;
	}
	zm_write_header(buff,len);
	zm_write_content(pdata,content_len,buff,len);
	zm_write_tail(buff,len);

	log_packet_content(LOG_SEND_PREFIX,buff,*len);

	return 0;
}

int zm_build_get_status(char *buff,int *len)
{
	if(buff==NULL||*len<0)
	{
		dzlog_error("invalid params!\n");
		return -1;
	}

	buff[0] = GET_DEV_STATUS;
	*len = 1;
	zm_build_packet(buff,len);

	return 0;
}

int zm_build_setip(const char *devid,IP_Array *ipaddrs, char *buff, int *len)
{
	if(devid == NULL || ipaddrs==NULL || buff==NULL||*len<0)
	{
		dzlog_error("invalid params!\n");
		return -1;
	}
	
	int i, size;
	const char *ip_array[4]={
		ipaddrs->dev_ip,
		ipaddrs->subnet,
		ipaddrs->defgw,
		ipaddrs->dnsip
	};

	buff[0] = SET_DEV_IPCFGS;
	size = DEV_ID_LEN; //strlen(devid);
	memcpy(buff+1, devid, size);
	*len = 1+size;
	buff[(*len)++] = ';';

	for(i=0;i<4;i++)
	{
		size = strlen(ip_array[i]);
		memcpy(buff+*len, ip_array[i], size);
		*len += size; 
		buff[(*len)++] = ';';
	}
	buff[(*len)-1] = 0;//del last ';' sign
	*len -= 1;

	zm_build_packet(buff,len);
	
	return 0;
}

int zm_build_get_dns(char *buff,int *len)
{
	unsigned char tmp[256]={0};

	if(buff==NULL||*len<0)
	{
		dzlog_error("invalid params!\n");
		return -1;
	}

	buff[0] = GET_DEV_DNSINF;
	// DES encryption about 'DEMO_DNS'
	desEncode((unsigned char *)DES_SUBKEY,strlen(DES_SUBKEY),(unsigned char *)DEMO_DNS,strlen(DEMO_DNS),tmp,256);
	*len = 1+strlen((const char*)tmp);
	zm_build_packet(buff,len);

	return 0;
}

int zm_build_getp2p(char *buff,int *len)
{
	if(buff==NULL||*len<0)
	{
		dzlog_error("invalid params!\n");
		return -1;
	}

	buff[0] = GET_P2P_STATUS;
	*len = 1;
	zm_build_packet(buff,len);

	return 0;
}

int zm_build_set_netcfg(const char *devid, Port_NetCfg *port_net,char *buff,int *len)
{
	if(devid==NULL||buff==NULL)
	{
		dzlog_error("invalid params!\n");
		return -1;
	}

	int size;

	size = DEV_ID_LEN;
	buff[0] = SET_OTH_NETCFG;
	memcpy(buff+1, devid, size);
	*len = 1+size;
	buff[(*len)++] = port_net->dhcp_flag;
	buff[(*len)++] = port_net->video_port&0xff;
	buff[(*len)++] = (port_net->video_port>>8)&0xff;
	buff[(*len)++] = port_net->web_port&0xff;
	buff[(*len)++] = (port_net->web_port>>8)&0xff;

	zm_build_packet(buff,len);

	return 0;
}

/*
 * parse received packets
 */
static int zm_check_packet_valid(const char *buff,int len)
{
	unsigned char ret = 0;

	if(buff[0]!=CMD_HEADER_F || buff[1]!=CMD_HEADER_S || buff[len-1]!=CMD_TAIL)
	{
		dzlog_error("packet header or tail is wrong:0x%x 0x%x ... 0x%x\n",
				buff[0],buff[1],buff[len-1]);
		return -1;
	}
	if((ret = zm_calc_checksum(buff,len-2)) != (unsigned char)buff[len-2])
	{
		dzlog_error("packet checksum is wrong:0x%x[calc]!= 0x%x[recv]\n",
				ret,buff[len-2]);
		return -1;
	}

	return 0;
}

static int zm_parse_res_stat(const char *buff,int len,void *data, int *data_len)
{
	Device_Stat_Info *pinfo = (Device_Stat_Info*)data;
	int i,j,size;
	int pos[10]={-1}; // the delimiter ';' position
	char tmp[16]={0};

	*data_len = sizeof(Device_Stat_Info);
	memset(pinfo,0,*data_len);
	j = 0;
	for(i=PACKET_DATA_POS;i<len-2;i++)
	{
		if(buff[i]==';')
			pos[j++] = i;
		if(j >= 10)
			return -1;// data content in packet has error
	}
	for(i=0;i<j;i++)
	{
		if(pos[i]<0)
			return -1;// data content in packet is less
	}
	// copy devid
	size = pos[0]-PACKET_DATA_POS;
	memcpy(pinfo->dev_id,buff+PACKET_DATA_POS,size);
	// copy dev ip
	size = pos[1]-pos[0]-1;
	memcpy(pinfo->ipaddrs.dev_ip,buff+pos[0]+1,size);
	// copy subnet ip
	size = pos[2]-pos[1]-1;
	memcpy(pinfo->ipaddrs.subnet,buff+pos[1]+1,size);
	// copy gateway ip
	size = pos[3]-pos[2]-1;
	memcpy(pinfo->ipaddrs.defgw,buff+pos[2]+1,size);
	// copy dns ip
	size = pos[4]-pos[3]-1;
	memcpy(pinfo->ipaddrs.dnsip,buff+pos[3]+1,size);
	// copy version
	size = pos[5]-pos[4]-1;
	memcpy(pinfo->version,buff+pos[4]+1,size);
	if(pinfo->version[size-1]=='\n')// last char is '\n'
		pinfo->version[size-1]=0;
	// copy web port
	size = pos[6]-pos[5]-1;
	memcpy(tmp,buff+pos[5]+1,size);
	tmp[size]=0;
	pinfo->port_info.web_port = atoi(tmp);
	// copy mac address
	size = pos[7]-pos[6]-1;
	memcpy(pinfo->macaddr,buff+pos[6]+1,size);
	// copy video port
	size = pos[8]-pos[7]-1;
	memcpy(tmp,buff+pos[7]+1,size);
	tmp[size]=0;
	pinfo->port_info.video_port = atoi(tmp);
	// copy dhcp flag
	size = (len-2)-pos[8]-1;
	memcpy(tmp,buff+pos[8]+1,size);
	tmp[size]=0;
	pinfo->port_info.dhcp_flag = atoi(tmp);

	return 0;
}

static int zm_parse_res_set(const char *buff,int len,void *data, int *data_len)
{
	char *pid = (char *)data;

	memset(pid, 0, *data_len);
	memcpy(pid,buff+PACKET_DATA_POS,len-7);
	*data_len = len - 7;

	return 0;
}

static int zm_parse_res_p2p(const char *buff,int len,void *data, int *data_len)
{
	Device_Stat_Info *pinfo = (Device_Stat_Info*)data;
	int i,j,size;
	int pos[9]={-1}; // the delimiter ';' position
	char tmp[16]={0};

	*data_len = sizeof(Device_Stat_Info);
	memset(pinfo,0,*data_len);
	j = 0;
	for(i=PACKET_DATA_POS;i<len-2;i++)
	{
		if(buff[i]==';')
			pos[j++] = i;
		if(j >= 9)
			return -1;// data content in packet has error
	}
	for(i=0;i<j;i++)
	{
		if(pos[i]<0)
			return -1;// data content in packet is less
	}
	// copy devid
	size = pos[0]-PACKET_DATA_POS;
	memcpy(pinfo->dev_id,buff+PACKET_DATA_POS,size);
	// copy dev ip
	size = pos[1]-pos[0]-1;
	memcpy(pinfo->ipaddrs.dev_ip,buff+pos[0]+1,size);
	// copy subnet ip
	size = pos[2]-pos[1]-1;
	memcpy(pinfo->ipaddrs.subnet,buff+pos[1]+1,size);
	// copy gateway ip
	size = pos[3]-pos[2]-1;
	memcpy(pinfo->ipaddrs.defgw,buff+pos[2]+1,size);
	// copy dns ip
	size = pos[4]-pos[3]-1;
	memcpy(pinfo->ipaddrs.dnsip,buff+pos[3]+1,size);
	// copy version
	size = pos[5]-pos[4]-1;
	memcpy(pinfo->version,buff+pos[4]+1,size);
	if(pinfo->version[size-1]=='\n')// last char is '\n'
		pinfo->version[size-1]=0;
	// copy web port
	size = pos[6]-pos[5]-1;
	memcpy(tmp,buff+pos[5]+1,size);
	tmp[size]=0;
	pinfo->port_info.web_port = atoi(tmp);
	// copy mac address
	size = pos[7]-pos[6]-1;
	memcpy(pinfo->macaddr,buff+pos[6]+1,size);
	// copy video port
	size = (len-2)-pos[7]-1;
	memcpy(tmp,buff+pos[7]+1,size);
	tmp[size]=0;
	pinfo->port_info.video_port = atoi(tmp);

	return 0;
}

static int zm_parse_res_dns(const char *buff,int len, void *data, int *data_len)
{
	unsigned char tmp[128]={0};// store recvbuff data
	int ret = 0;
	Device_DNS_Info *pdnsinfo;
	char *token = NULL,*str1 = NULL, *psavestr=NULL;

	ret = desDecode((unsigned char *)DES_SUBKEY,strlen(DES_SUBKEY),(unsigned char *)(buff+PACKET_DATA_POS),len-7,tmp,128);
	if(ret < 0)
	{
		dzlog_error("desDecode error,ret = %d\n",ret);
		return -1;
	}

	// data string: "id;dns-name;code"
	memset(data,0,*data_len);
	pdnsinfo = (Device_DNS_Info *)data;
	*data_len = sizeof(Device_DNS_Info);
	// copy data
	str1 = tmp;
	token = strtok_r(str1,";",&psavestr);
	memcpy(pdnsinfo->dev_id,token,DEV_ID_LEN);
	token = strtok_r(NULL,";",&psavestr);
	memcpy(pdnsinfo->dnsname,token,strlen(token));
	token = strtok_r(NULL,";",&psavestr);
	memcpy(pdnsinfo->code,token,strlen(token));

	return 0;
}

int zm_parse_packet(const char *buff, void *data, int *data_len,unsigned char reqcmd)
{
	if(buff == NULL)
	{
		dzlog_error("invalid params!\n");
		return -1;
	}

	if(zm_check_res_cmd(buff[PACKET_CMD_POS]) < 0)
	{
		dzlog_error("invalid zm spec response cmd:%d\n",buff[0]);
		return -1;
	}
	
	if(zm_check_req_cmd(reqcmd) < 0)
	{
		dzlog_error("invalid zm spec request cmd:%d\n",reqcmd);
		return -1;
	}

	int len = 0;
	int rescmd = buff[PACKET_CMD_POS];
	len = (buff[3]<<8)|buff[2];

	dzlog_debug("rescmd:0x%x, len:%d;reqcmd:0x%x.\n",rescmd,len,reqcmd);
	log_packet_content(LOG_RECV_PREFIX,buff,len);

	if(zm_check_packet_valid(buff,len) < 0)
	{
		dzlog_error("invalid zm spec packet!\n");
		return -1;
	}
	switch(reqcmd)
	{
		case GET_DEV_STATUS:
			if(rescmd != RES_DEV_STATUS) 
			{
				dzlog_error("dismatch response cmd.\n");
				return -1;
			}
			if(data==NULL||*data_len<sizeof(Device_Stat_Info)) 
			{
				dzlog_error("need Device_Stat_Info struct in-param!\n");
				return -1;
			}
			return zm_parse_res_stat(buff,len,data,data_len);

		case SET_DEV_IPCFGS:
			if(rescmd != RES_DEV_IPCFGS)
			{
				dzlog_error("dismatch response cmd.\n");
				return -1;
			}
			if(data==NULL||*data_len < DEV_ID_LEN) return -1;
			return zm_parse_res_set(buff,len,data,data_len);
		case GET_DEV_DNSINF:
			if(rescmd != RES_DEV_DNSINF)
			{
				dzlog_error("dismatch response cmd.\n");
				return -1;
			}
			if(data==NULL||*data_len < sizeof(Device_DNS_Info)) return -1;
			return zm_parse_res_dns(buff,len,data,data_len);
		case GET_P2P_STATUS:
			if(rescmd != RES_P2P_STATUS)
			{
				dzlog_error("dismatch response cmd.\n");
				return -1;
			}
			if(data==NULL||*data_len < sizeof(Device_Stat_Info)) return -1;
			return zm_parse_res_p2p(buff,len,data,data_len);
		case SET_OTH_NETCFG:
			if(rescmd != RES_OTH_NETCFG)
			{
				dzlog_error("dismatch response cmd.\n");
				return -1;
			}
			if(data==NULL||*data_len < DEV_ID_LEN) return -1;
			return zm_parse_res_set(buff,len,data,data_len);

		default:
			return -1;
	}
	
	return 0;
}

